package com.xiaoyun.lzg.controller;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.xiaoyun.lzg.strategy.SuffixStrategy;
import com.xiaoyun.lzg.strategy.SuffixType;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.apache.any23.encoding.TikaEncodingDetector;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static cn.hutool.core.text.CharSequenceUtil.startWith;

/**
 * 1. 用来判断包路径是否存在，若不存在，即创建文件夹；
 * 2. 将配好的模板文件写出到指定路径下的文件
 * 3. 从数据库获取表信息
 */
@Component
@Slf4j
public class FreeMarkerUtil {
    //获取工程主目录
    private static final String WORK_DIR = System.getProperty("user.dir");
    //输出包名
    private static final String basePackageUrl = "com.xiaoyun.lzg.bean";


    @Autowired
    Configuration freeMarker;

    public void initMap(String fileName, Map<String, Object> root) {
        String workerUrl = WORK_DIR + (".src.main.java." + basePackageUrl + ".").replaceAll("\\.", Matcher.quoteReplacement(File.separator));
        String entityName = fileName.replace(".txt", "");
        root.put("basePackageUrl", basePackageUrl);
        root.put("saveUrl", workerUrl);
        root.put("entityName", entityName);
    }

    /**
     * 判断包路径是否存在
     */
    private void pathJudgeExist(String path) {
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
     * 输出到文件
     */
    public void printFile(Map<String, Object> root, Template template, String filePath, String fileName) throws IOException, TemplateException {
        pathJudgeExist(filePath);
        File file = new File(filePath, fileName);
        if (!file.exists()) {
            file.createNewFile();
        }
        Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8));
        template.process(root, out);
        out.close();
    }

    /**
     * 首字母大写
     */
    public String capFirst(String str) {
        if (str.length() > 1) {
            return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
        }
        return str.toUpperCase();
    }

    public String lowerFirst(String str) {
        if (str.length() > 1) {
            return str.substring(0, 1).toLowerCase() + str.substring(1);
        }
        return str.toUpperCase();

    }

    /**
     * 下划线命名转为驼峰命名
     */
    public String underlineToHump(String para) {
        StringBuilder result = new StringBuilder();
        String[] a = para.split("_");
        for (String s : a) {
            if (result.length() == 0) {
                result.append(s);
            } else {
                result.append(s.substring(0, 1).toUpperCase());
                result.append(s.substring(1).toLowerCase());
            }
        }
        return result.toString();
    }


    /**
     * 生成代码
     */
    public void generate(Map<String, Object> root, String templateName, String saveUrl, String entityName) throws IOException, TemplateException {
        //获取模板
        Template template = freeMarker.getTemplate(templateName);
        //输出文件
        printFile(root, template, saveUrl, entityName);
    }

    public void generate(Map<String, Object> root) throws IOException, TemplateException {

        String entityName = (String) root.get("entityName");
        String workerUrl = (String) root.get("saveUrl");
        generate(root, "entity.ftl", workerUrl, entityName + ".java");
    }


    /**
     * 集合是否为空
     *
     * @param collection 集合
     * @return 是否为空
     */
    public boolean isEmptyCollection(Collection<?> collection) {
        return collection == null || collection.isEmpty();
    }

    public boolean isBlank(CharSequence str) {
        int length;

        if ((str == null) || ((length = str.length()) == 0)) {
            return true;
        }

        for (int i = 0; i < length; i++) {
            // 只要有一个非空字符即为非空字符串
            if (false == CharUtil.isBlankChar(str.charAt(i))) {
                return false;
            }
        }

        return true;
    }

    /**
     * @param uploadTemp
     * @param txtName
     * @return
     */
    public Map<String, Object> getEntity(String uploadTemp, String txtName) {

        List<String> strings = null;
        try {
            strings = getFileReader(uploadTemp);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return getResultMap(txtName, strings);
    }

    public Map<String, Object> getResultMap(String txtName, List<String> strings) {
        if (filterCheck(strings, txtName)) {
            return new HashMap<>();
        }
        Map<String, Object> map = new LinkedHashMap<>();
        String firstString = strings.get(0);
        String[] columns = firstString.split("\t");
        String[] columnsDesc = strings.get(1).split("\t");

        String id = columns[0];
        if (isBlank(id)) {
            map.put("id", "Id");
        }
        //除去 "\uFEFF"
        if (id.startsWith("\uFEFF")) {
            id = id.substring(1);
        }
        String idLower = id.toLowerCase();
        if (idLower.contains("id") && !idLower.contains("hide")) {
            String idTarget = upId(id, idLower);
            map.put("id", lowerFirst(idTarget));
        } else {
            map.put("id", lowerFirst(id));

        }
        map.put("idDesc", columnsDesc[0]);
        map.put("entityName", txtName.replace(".txt", ""));

        List<Map<String, Object>> list = new LinkedList<>();
        boolean flag = false;
        Set<String> set = new HashSet<>();
        for (int i = 1; i < columns.length; i++) {
            Map<String, Object> columnsMap = new HashMap<>();
            if (isBlank(columns[i])) {
                if (isBlank(columnsDesc[i])) {
                    continue;
                } else {
                    setError(txtName, StrUtil.format("当前字段有空值,位置在第:{}个,其前一个字段为:{}", i + 1, columns[i - 1]), null);
                    flag = true;
                    break;
                }
            }
            //判断是否有相同属性,
            if (set.add(columns[i])) {
                String lowerColumn = columns[i].toLowerCase();

                convertColumns(columns, i, columnsMap, lowerColumn);
                columnsMap.put("columnDesc", columnsDesc[i]);
                list.add(columnsMap);
            } else {
                setError(txtName, StrUtil.format("当前类存在相同属性,属性名为:{}", columns[i]), null);
            }
        }
        if (flag) {
            return null;
        }
        map.put("columns", list);
        return map;
    }

    private void setError(String s1, String s2, String s3) {
        ErrorBean errorBean = new ErrorBean(s1, s2, s3);
        String jsonStr = JSONUtil.toJsonStr(errorBean);
        String oldMsg = UserContext.get();
        if (isBlank(oldMsg)) {
            UserContext.set(jsonStr);
        } else {
            UserContext.set(oldMsg + "," + jsonStr);
        }
    }


    private List<String> getFileReader(String uploadTemp) throws IOException {
        return readLines(new FileInputStream(new File(uploadTemp)));
    }

    public List<String> readLines(InputStream inputStream) throws IOException {
        BufferedInputStream is = new BufferedInputStream(inputStream);
        is.mark(1000000);//10M
        //第一次处理stream，得到encoding
        String encoding = new TikaEncodingDetector().guessEncoding(is);
        is.reset();
        List<String> strings = IOUtils.readLines(is, encoding);
        return strings;
    }

    private boolean filterCheck(List<String> strings, String txtName) {
        if (isEmptyCollection(strings)) {
            return true;
        }
        String firstString = strings.get(0);
        //去除 ﻿ \ufeff 非法字符
        if (firstString.startsWith("\uFEFF")) {
            firstString = firstString.substring(1);
        }
        if (isFuncAndNum(firstString)) {
            return true;
        }

        String[] columns = firstString.split("\t");
        String[] columnsDesc = strings.get(1).split("\t");
        if (columns.length != columnsDesc.length) {
            setError(txtName, StrUtil.format("当前文件格式错误,备注不匹配,:{},:{}", columns, columnsDesc), null);
            return true;
        }


        String id = columns[0];

        if (columns.length == 1 && id.length() == 1) {
            return true;
        }
        return false;
    }

    public boolean contains(CharSequence str, CharSequence searchStr) {
        if (null == str || null == searchStr) {
            return false;
        }
        return str.toString().contains(searchStr);
    }

    private boolean isFuncAndNum(String firstString) {
        return isBlank(firstString) || startWithNum(firstString) || startWithAny(firstString, "func");
    }


    private boolean startWithNum(String firstString) {
        return startWithAny(firstString, "1", "2", "3", "4", "5", "6", "7", "8", "9", "0");
    }

    public boolean startWithAny(CharSequence str, CharSequence... prefixes) {
        if (isEmpty(str) || ArrayUtil.isEmpty(prefixes)) {
            return false;
        }

        for (CharSequence suffix : prefixes) {
            if (startWith(str, suffix, false)) {
                return true;
            }
        }
        return false;
    }


    public boolean containsAny(CharSequence str, CharSequence... testStrs) {
        return null != getContainsStr(str, testStrs);
    }

    public String getContainsStr(CharSequence str, CharSequence... testStrs) {
        if (isEmpty(str) || ArrayUtil.isEmpty(testStrs)) {
            return null;
        }
        for (CharSequence checkStr : testStrs) {
            if (str.toString().contains(checkStr)) {
                return checkStr.toString();
            }
        }
        return null;
    }

    public boolean isEmpty(CharSequence str) {
        return str == null || str.length() == 0;
    }

    public static Boolean isNum(char c) {
        Boolean flag = null;
        try {
            flag = true;
            Pattern pattern = Pattern.compile("[0-9]*");
            Matcher result = pattern.matcher(c + "");
            if (!result.matches()) {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return flag;
    }


    private boolean startAndEndHasNum(String name) {
        return isNum(name.charAt(0)) || isNum(name.charAt(name.length() - 1)) || containsAny(name, "(", ")");
    }

    /**
     * 获取属性是否含有 Id字段
     * 是转为 Integer类型 并且把ID转为Id
     * <p>
     * 获取属性是否含有 Time字段
     * 是转为 Date类型
     *
     * @param columns
     * @param i
     * @param columnsMap
     * @param lowerColumn
     */
    private void convertColumns(String[] columns, int i, Map<String, Object> columnsMap, String lowerColumn) {
        if (lowerColumn.contains("id") && !lowerColumn.contains("hide")) {
            String columnName = upId(columns[i], lowerColumn);
            columnsMap.put("columnName", lowerFirst(columnName));
            columnsMap.put("columnType", Integer.class.getSimpleName());
        } else if (lowerColumn.contains("time")) {
            columnsMap.put("columnType", Date.class.getSimpleName());
            columnsMap.put("columnName", lowerFirst(columns[i]));
        } else {
            columnsMap.put("columnType", String.class.getSimpleName());
            columnsMap.put("columnName", lowerFirst(columns[i]));
        }
    }

    /**
     * 更改Id
     *
     * @param column      原值
     * @param lowerColumn 全变小写的值
     * @return
     */
    private String upId(String column, String lowerColumn) {
        return upAttribute(column, lowerColumn, "Id");
    }

    private String upAttribute(String column, String lowerColumn, String attribute) {
        int editIndex = lowerColumn.lastIndexOf(attribute.toLowerCase());
        int length = attribute.length();
        String columnName = column;
        if (lowerColumn.length() > editIndex + length) {
            columnName = columnName.substring(0, editIndex) + attribute + columnName.substring(editIndex + length);
        } else {
            columnName = columnName.substring(0, editIndex) + attribute;
        }
        return columnName;
    }

    /**
     * 缓存io流到临时文件
     * 默认目录在 工作目录下temp文件下
     *
     * @param is
     * @param txtName
     * @return
     */
    public String outTemp(InputStream is, String txtName) {
        String uploadTemp = getTempName(txtName);
        BufferedOutputStream outputStream = FileWriter.create(new File(uploadTemp)).getOutputStream();
        IoUtil.copy(is, outputStream);
        return uploadTemp;
    }

    public String getTempName(String txtName) {
        String time = System.currentTimeMillis() + "";
        return System.getProperty("user.dir") + File.separator + "temp" + File.separator + time + "-" + txtName;
    }


    public void listTxT(File path, Map<String, String> map) {
        if (!path.exists()) {
            log.info("文件名称不存在!");
        } else {
            if (path.isFile()) {
                String name = path.getName();
                if (nameRule(name)) {
                    map.put(name, path.getAbsolutePath());
                }
            } else {
                File[] files = path.listFiles();
                for (int i = 0; i < files.length; i++) {
                    listTxT(files[i], map);
                }
            }
        }
    }

    private boolean nameRule(String name) {
        return name.endsWith(".txt") && !startAndEndHasNum(name.replace(".txt", ""));
    }

    /**
     * public List addDate(InputStream inputStream, String originalFilename) throws IOException, TemplateException {
     * FileReader fileReader = getFileReader(inputStream, originalFilename);
     * List<String> str = fileReader.readLines();
     * List<TaskListNew> list = new LinkedList();
     * <p>
     * Map<String, Object> map = getResultMap(originalFilename, str);
     * <p>
     * getDate(str, list, map, originalFilename);
     * return list;
     * }
     * <p>
     * public void getDate(List<String> str, List<TaskListNew> list, Map<String, Object> map, String originalFilename) throws IOException, TemplateException {
     * if (map != null && map.size() > 0) {
     * String id = (String) map.get("id");
     * List<Map<String, Object>> columnKeys = (List<Map<String, Object>>) map.get("columns");
     * for (int i = 2; i < str.size(); i++) {
     * String[] columnValues = str.get(i).split("\t");
     * if (columnKeys.size() < (columnValues.length - 1)) {
     * break;
     * }
     * TaskListNew taskListNew = new TaskListNew();
     * if (!isBlank(columnValues[0])) {
     * ReflectUtil.invoke(taskListNew, StrUtil.genSetter(id), Integer.valueOf(columnValues[0]));
     * }
     * for (int j = 1; j < columnValues.length; j++) {
     * String columnName = (String) columnKeys.get(j).get("columnName");
     * String columnType = (String) columnKeys.get(j).get("columnType");
     * <p>
     * String columnValue = columnValues[j];
     * if (!isBlank(columnValue)) {
     * if (!columnType.toLowerCase().contains("list") && columnValue.contains("|")) {
     * Map<String, Object> columnMap = columnKeys.get(j);
     * columnMap.put("columnType", "List<Integer>");
     * initMap(originalFilename, map);
     * generate(map);
     * getDate(str, list, map, originalFilename);
     * return;
     * }
     * if ("integer".equalsIgnoreCase(columnType) && columnValue.contains("=")) {
     * Map<String, Object> columnMap = columnKeys.get(j);
     * columnMap.put("columnType", "String");
     * initMap(originalFilename, map);
     * generate(map);
     * getDate(str, list, map, originalFilename);
     * return;
     * }
     * if (columnType.toLowerCase().contains("list")) {
     * if (columnValue.contains("|")) {
     * String[] split = columnValue.split("\\|");
     * List<Integer> listInteger = Arrays.stream(split).map(Integer::parseInt).collect(Collectors.toList());
     * ReflectUtil.invoke(taskListNew, StrUtil.genSetter(columnName), listInteger);
     * }
     * } else {
     * if ("integer".equalsIgnoreCase(columnType)) {
     * ReflectUtil.invoke(taskListNew, StrUtil.genSetter(columnName), Integer.valueOf(columnValue));
     * } else {
     * ReflectUtil.invoke(taskListNew, StrUtil.genSetter(columnName), columnValue + "");
     * }
     * }
     * }
     * }
     * list.add(taskListNew);
     * }
     * }
     * }
     **/


}
